package com.airshare;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;


// A fileserver:
// 1. Starts on any available port
// 2. Serves files from a given share_directory - no subdir support yet
public class FileServer extends Thread  implements FSConstants{

	ServerSocket socket;
	Socket connection;
	ShareDir shareDirectory;
	boolean running;
	private int port;
	private String userName;
	
	public FileServer(String user, ShareDir shareDir) {
		
		super("FileServer");
		
		shareDirectory = shareDir;
		userName = user;
		
		// Also get sockets ready
		try {
			socket = new ServerSocket(0);
			port = socket.getLocalPort();
		}
		catch(Exception e){
			e.printStackTrace();
		}
		running = false;
	}

	public int getPort() {
		return port;
	}
	
	@Override
	public void run() {
		running = true;
		while(running) {
			try{
				connection = socket.accept();
				new FileServerThread(connection).start();
			}
			catch (Exception e) {
			}
		}
	}

	public synchronized void kill() {
		running = false;
	}
	
	private class FileServerThread extends Thread {

		private boolean done = false;
		private Socket sock;
		DataInputStream dis;
		DataOutputStream dos;
		FSMessage reply;
		
		public FileServerThread(Socket connection) {
			this.sock = connection;
			//System.out.println("S: Client connected...");
			
			try {
				dis = new DataInputStream(connection.getInputStream());
				dos = new DataOutputStream(connection.getOutputStream());
			} catch (IOException e) { 
				e.printStackTrace();
			}
		}

		public void run() {
            byte[] data;
            while(!done)
            {
             	try {
             		data = new byte[MAX_MESSAGE_SIZE];
             		dis.read(data);
             		FSMessage message = ((FSMessage)FSUtil.bytesToObject(data));
             		//System.out.println("S: Received msg " + message._messageType);
             		processMessage(message);
             	}
             	catch(Exception e) {
             		done = true;
             		try {
             			sock.close();
             		} 
             		catch(Exception se) {
                         System.err.println("ERROR CLOSING SOCKET "+se);
             		}
             	}
            } //End while                        			
		}

		private synchronized void processMessage(FSMessage msg) {

			reply = new FSMessage();
			reply._dstUser = msg._srcUser;
			reply._srcUser = FileServer.this.userName;
			reply._fileSize = 0L;
			reply._fileName = msg._fileName;
			reply._messageType = MSG_INVAL;
			reply._indexSize = 0;

			switch(msg._messageType) {
			
			case REQ_HELLO:
				helloRequest();
				break;
			
			case REQ_INDEX_SIZE:
				indexSizeRequest();
				break;
				
			case REQ_INDEX:
				indexRequest();
				break;
				
			case REQ_FILE_SIZE:
				filesizeRequest();
				break;
				
			case REQ_FILE:
				filesendRequest();
				break;
			}
		}

		private void indexSizeRequest() {
			byte [] data = null;
			
			try {
				data = FSUtil.objectToBytes(FileServer.this.shareDirectory.getIndex());
				reply._messageType = RES_INDEX_SIZE;
				reply._indexSize = data.length;
				sendReply();
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}

		private void sendReply() {
			byte [] data;
			try {
				//System.out.println("S: sending reply msg " + reply._messageType);
				data = FSUtil.objectToBytes(reply);
				dos.write(data, 0, data.length);
				dos.flush();
			}
			catch (Exception e) {
				e.printStackTrace();
			}			
		}

		private void indexRequest() {
			byte [] data = null;
			try {
				data = FSUtil.objectToBytes(FileServer.this.shareDirectory.getIndex());
				dos.write(data);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}	

		private void filesendRequest() {		
			// Stream the file out
			FileServer.this.shareDirectory.sendFile(dos, reply._fileName);
		}

		private void filesizeRequest() {
			
			if (!FileServer.this.shareDirectory.fileExists(reply._fileName)) {
				reply._messageType = RES_ERR_FILE;
				sendReply();
				return;
			}
			
			reply._messageType = RES_FILE_SIZE;
			reply._fileSize = FileServer.this.shareDirectory.getFileSize(reply._fileName);
			sendReply();
		}

		private void helloRequest() {
			reply._messageType = RES_HELLO;
			sendReply();
		}
	} // End class FileServerThread	
} // End class FileServer
